/** 
 * Simplified Pascal in the EBNF:
 * 
 * program -> PROGRAM ID ';' block '.'
 * 
 * block -> declarations modules stmtblock  -- NAO EXISTE MAIS PROCEDURE DENTRO DE PROCEDURE
 * 
 * declarations -> 
 *    [CONST ID = constant';' {ID = constant';'}]
 *    [TYPE ID = type';' {ID = type';'}]
 *    [VAR idlist ':' type';' {idlist ':' type';'}]
 *    
 *
 * modules -> {procedure | function}
 * 
 * procedure -> PROCEDURE ID formalparm';' declarations stmtblock ';'  -- NAO EXISTE MAIS PROCEDURE DENTRO DE PROCEDURE
 * 
 * function -> FUNCTION ID formalparm ':' typeid ';'declarations stmtblock';' -- NAO EXISTE MAIS PROCEDURE DENTRO DE PROCEDURE
 * 
 * stmtblock -> BEGIN stmtlist END
 * 
 * stmtlist -> stmt {';' stmt}
 * 
 * stmt -> stmtblock | ifstmt | whilestmt | repstmt | forstmt | gotostmt | casestmt | idstmt
 * 
 * idstmt -> ID
 */

/** further simplifications for today only:
 * 
 * program -> PROGRAM ID ';' block '.'
 * 
 * block -> declarations stmtblock
 * 
 * declarations -> [VAR idlist ':' type';' {idlist ':' typeid';'}]
 *    modules
 *
 * modules -> {procedure | function}
 * 
 * procedure -> PROCEDURE ID formalparm';' block ';'
 * 
 * function -> FUNCTION ID formalparm ':' typeid ';'block';'
 * 
 * stmtblock -> BEGIN stmtlist END
 * 
 * stmtlist -> stmt {';' stmt}
 * 
 * stmt -> stmtblock | ifstmt | whilestmt | repstmt | idstmt | ""
 */

#include <stdio.h>
#include <string.h>

#include "parser.h"
#include "keywords.h"
#include "tokens.h"
#include "lexer.h"


int sympos;
int objtype;
int acctype,returnedtype;
int symbol_entry;
int errflag;
int offset;

FILE *target;


void
pas(void)
{

    program();


}

/* program -> PROGRAM ID ';' block '.'*/
void program(void)
{
  match(PROGRAM); 
  
  match(ID);
  match(';');
  block();
  match('.');  
}

/* block -> declarations stmtblock*/
void block(void)
{declarations();stmtblock();}
extern int symtab_nextentry;
/* declarations -> 
*    [VAR idlist ':' type';' {idlist ':' type';'}]
*    modules*/



void declarations(void)
{
  if(lookahead==VAR){
    match(VAR);
rep_idlist:
   
    idlist();
    match(':');
    typeid(); 
       
    match(';');
    if(lookahead==ID) goto rep_idlist;
    
    
  }
  modules();
  
}
 
/* modules -> {procedure | function} */
void modules(void)
{
  while(lookahead==PROCEDURE || lookahead==FUNCTION){
    if(lookahead==PROCEDURE)procedure();
    else function();
  }
}

/* procedure -> PROCEDURE ID formalparm';' block ';'*/
void procedure(void) 
{
 
  match(PROCEDURE);
  match(ID);formalparm();match(';');
  block();
  match(';');
  
   
}



/* formalparm -> '(' idlist ':' typeid {; idlist ':' typeid}  ')' ';' */
void formalparm(void){
  match('(');
  idlist(); match(':'); typeid();
  while(lookahead == ';'){
    match(';');
    idlist(); match(':'); typeid();
  }
  match(')'); match(';');
    
}


 /* function -> FUNCTION ID formalparm ':' typeid ';'block';'*/
void function(void)
{
 
  match(FUNCTION);
  
  match(ID);  
  formalparm();match(':');
  int tmptype = 0;//typheck de procedimee(4);
  match(';');
  block();
  match(';');
  
}

#define MAXSYMLIST 0x10000
#define MAXIDSIZE 32
char symlist[MAXSYMLIST][MAXIDSIZE];
int sympos;

/* idlist -> ID {','ID}*/
void idlist(void)
{
  
  match(ID);
  while(lookahead==','){
    match(',');
    
    match(ID);
  }
}

/*typeid -> INTEGER | REAL */
int typeid(void)
{
 /** datatype **/  int dtype, i; 
  switch(lookahead){
    case INTEGER:
      match(dtype=INTEGER);
      return INTEGER;
      break;
    default:
      match(dtype=REAL);
      return REAL;
  }
  
 
}

void idstmt(void )
{

}

/* stmtblock -> BEGIN stmtlist END */
void stmtblock(void)
{
  match(BEGIN);stmtlist();match(END);
}

/* stmtlist -> stmt {';'stmt} */
void stmtlist(void)
{
  stmt();
  while(lookahead==';'){
    match(';');stmt();
  }
}

/* ifstmt -> IF expression THEN stmt[ELSE stmt]*/
void ifstmt(void)
{
  match(IF); expression(); match(THEN);
    stmt();
  if(lookahead==ELSE) {
    match(ELSE);stmt();
  }
}

/* whilestmt -> WHILE expression DO stmt */
void whilestmt(void)
{
  match(WHILE);expression();match(DO);
    stmt();
  
  }

/*repstmt -> REPEAT stmtlist UNTIL expression */
void repstmt(void)
{
  match(REPEAT);
  stmtlist();
  match(UNTIL);
  expression();
}

/*forstmt-> FOR assign [TO|DOWNTO] expression DO stmt */

void forstmt(void)
{
  match(FOR);
  assign();
  if (lookahead==TO)
    match(TO);
  else
    match(DOWNTO);
    expression();
    match(DO);
    stmt();
  
}
/* stmt -> stmtblock | ifstmt | whilestmt | repstmt | idstmt | "" */
void stmt(void)
{
  switch(lookahead){
    case BEGIN:
      stmtblock();
      break;
    case IF:
      ifstmt();
      break;
    case WHILE:
      whilestmt();
      break;
    case REPEAT:
      repstmt();
      break;
    case ID:
      idstmt();
      break;
  }
}

/*gotostm -> GOTO UINT*/ 
  
/*void gotostmt(void)
{
	match('GOTO'); match('UINT');
}*/

/*isrelationalop -> '=' | '>' | '=' | '>' | '<' | GEQ | LEQ | NEQ */
isrelationalop(token_t token)
{
  switch(token){
    case '=':
    case '>':
    case '<':
    case GEQ:
    case LEQ:
    case NEQ:
      return token;
  }
  return 0;
}

/* isaddop-> '+' |'-' | OR */
isaddop(token_t token)
{
  switch(token){
    case '+':
    case '-':
    case OR:
      return token;
  }
  return 0;
}

/* ismulop -> '*' | '/' | AND | DIV | MOD */ 
ismulop(token_t token)
{
  switch(token){
    case '*':
    case '/':
    case AND:
    case DIV:
    case MOD:
      return token;
  }
  return 0;
}

/* expression-> expr [isrelationalop expr]*/
void expression(void)
{
  
  
  expr();
  if(isrelationalop(lookahead)){ /* VAMOS ADMITIR QUE SEJA POSSIVEL COMPARAR INTEIRO COM PONTO FLUTUANTE*/
    match(lookahead); expr();  /*AQUI TERA QUE SER UMA VARIAVEL LOGICA*/
  }
  
}

/*expr-> [-] term {isaddop term} */
void expr(void)
{
 
  if(lookahead=='-'){
  
    
    match('-');
  }
  term();
 
  while(isaddop(lookahead)){
   
    match(lookahead); term();
  
  }
  
}

/* term-> factor {ismulop factor}*/
void term(void)
{
  int op;
  factor();
  while(ismulop(lookahead)){  
   op = lookahead;
    match(lookahead);
     
     factor();
  
    switch(op){
      case '*':
      break;
      case '/':
      break;
    }
  }
}

/* factor-> UINT | FLOAT | ID ['=' expr] |'(' exprlist ')'| NOT factor */
void factor(void){
  
  int obj;
    switch(lookahead){
      case UINT:
	
	match(UINT);
	break;
      case FLOAT:
	
        match(FLOAT);
	break;
      case ID:
	
	match(ID);
	if(lookahead=='('){
	  match('(');exprlist();match(')');
	
        }
        
        
        break;
      case '(':
        match('(');
	
	expression();
	
	match(')'); /*SE RETURNED TYPE FOR LÓGICO, O ACCTYPE SO PODE SER LOGICO*/
	/** at this point, expression has synthesized an attribute storing resulting data type in the expression. However, the just calculated attribute might be grater than the Left-attribute along the current expression, so that a promotion must accur (tipo int é menos complexo que ponto flutuante) 
	 ISSO VAI ACOTECER TAMBÉM PARA INTEIRO SEM SINAL, "PROMOCAO DE TIPO" **/
      break;
      default:
        match(NOT);factor(); /*PRECISA DE PONTO FLUTUANTE, SE APARECER UMA EXPRESSAO ARITMETICA NÃOPODE AFIRMAR NADA, SO NO FINL DO FATOR SABEREMOS SE O TIPO RETORNADO É LOGICO*/
    }
}

/*exprlist-> expression {',' expression}*/
void exprlist(void)
{
 
  expression();
  
  
  while(lookahead==','){
    match(',');expression();
 
   
  }
}
